package com.atguigu.lease.app.service.impl;

import com.atguigu.lease.app.interceptor.WebAppAuthInterceptor;
import com.atguigu.lease.app.mapper.ApartmentInfoMapper;
import com.atguigu.lease.app.service.BrowsingHistoryService;
import com.atguigu.lease.model.dto.app.RoomInfoQueryDto;
import com.atguigu.lease.model.vo.app.ApartmentItemVo;
import com.atguigu.lease.model.vo.app.RoomDetailVo;
import com.atguigu.lease.model.vo.app.RoomInfoItemVo;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.lease.model.bean.RoomInfo;
import com.atguigu.lease.app.service.RoomInfoService;
import com.atguigu.lease.app.mapper.RoomInfoMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.concurrent.TimeUnit;

/**
* @author 1
* @description 针对表【room_info(房间信息表)】的数据库操作Service实现
* @createDate 2025-09-28 11:32:26
*/
@Service
public class RoomInfoServiceImpl extends ServiceImpl<RoomInfoMapper, RoomInfo>
    implements RoomInfoService{
    //保存对象比较方便， 如果是自定义类型的对象 默认需要自定义类型实现序列化接口
    // 我们可以为他设置键和值的序列化器， 保存对象时 redisTemplate会自动将对象转为json字符串存储
    //  读取redis数据时，会自动将json字符串转为java对象使用
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    BrowsingHistoryService browsingHistoryService;
    @Autowired
    private ApartmentInfoMapper apartmentInfoMapper;
//    @Cacheable(value = "stgy:room:page" , key = "#roomInfoQueryDto.current+':'+#roomInfoQueryDto.size")
    @Override
    public Page<RoomInfoItemVo> getPage(RoomInfoQueryDto roomInfoQueryDto) {
        // 分页查询数据： 涉及多表联查、分页的驱动表联查的其他表 有1：n关系的映射，为了避免 N+1问题
        //  使用分步查询解决
        // 前端提交的条件需要通过多个表的字段过滤，所以我们需要联查这些表 并过滤数据
        return baseMapper.selectRoomInfoItemVoPage(new Page<RoomInfoItemVo>(roomInfoQueryDto.getCurrent(),
                        roomInfoQueryDto.getSize()),
                roomInfoQueryDto
        );
    }
    /*
        使用springcache 整合redis实现缓存自动管理：
            整合步骤：
                1、引入 spring-context依赖+ 缓存管理技术的依赖(redis启动器)
                2、启用springcache缓存管理：
                    组件类上添加注解： @EnableCaching
                3、为springcache配置缓存管理器：
                    springcache整合 第三方的缓存管理工具 实现缓存管理
                4、在需要使用缓存管理的业务方法上(一般都是查询方法)
                    @Cacheable :
                        - 先检查缓存 如果命中直接返回缓存结果
                        - 如果没有命中 执行标注的方法 将方法的结果存到缓存中
                    @CacheEvict ：
                        - 驱逐缓存， 一般标注在 增删改方法上，如果增删改方法对应的缓存数据过期了
                            它可以将缓存删除
                    @CachePut
                        - 保存缓存， 一般标注在更新 或者新增方法上，如果新增的结果 或者更新的结果 需要保存到缓存中
                        可以使用这个注解
     */
    //value+ key 可以拼接 保存方法返回值到redis中的键
    // value用来写固定字符串， key支持表达式 取出形参变量值
    //value = "stgy" , key="'room:'+#id" 拼接的键为  stgy::room:id值
    @Cacheable(value = "stgy" , key="'room:'+#id")
    @Override
    public RoomDetailVo getDetailById(Long id) {
        //1、查询房间详情
        RoomDetailVo  roomDetailVo = baseMapper.selectRoomDetailVoById(id);
        //2、为房间详情查询公寓详情
        ApartmentItemVo apartmentItemVo =  apartmentInfoMapper.selectApartmentItemVoById(roomDetailVo.getApartmentId());
        //3、将公寓数据设置给房间对象
        roomDetailVo.setApartmentItemVo(apartmentItemVo);

        //用户查询房间详情：可以保存这个用户的浏览记录
        Long userId = WebAppAuthInterceptor.getUserId();
        if(userId!=null){
            //用户已登录，保存浏览记录  可以异步执行！！！
            browsingHistoryService.saveBrowsingHistory(userId , id);
        }


        return roomDetailVo;
    }

    /*
        手动缓存管理：需要我们自己编写缓存管理的业务逻辑代码
            缺点：代码逻辑 冗余
                    每个需要缓存管理的业务方法都需要 编写一次
            解决：
                1、我们手写aop 缓存管理(！！！ 后面的两个项目都使用了！！！)
                2、spring框架 基于aop已经封装好了缓存管理
                        spring cache
                            - spring cache(缓存管理)和 spring task(定时任务) async(异步任务) 都是spring-context包提供的功能
     */
//    @Override
//    public RoomDetailVo getDetailById(Long id) {
//        //- 缓存管理1- 先查询缓存，如果存在直接返回
//        String cacheKey = "stgy:room:"+id;
//        Object result = redisTemplate.opsForValue()
//                .get(cacheKey);
//        if(!ObjectUtils.isEmpty(result)){
//            // jackson将对象转为json字符串时 会将对象的全类名存到json中，还原时使用json中的全类名还原对象
//            return (RoomDetailVo) result;
//        }
//        //- 缓存管理2- 不存在缓存，查询数据并保存到缓存中
//
//        //1、查询房间详情
//        RoomDetailVo  roomDetailVo = baseMapper.selectRoomDetailVoById(id);
//        //2、为房间详情查询公寓详情
//        ApartmentItemVo apartmentItemVo =  apartmentInfoMapper.selectApartmentItemVoById(roomDetailVo.getApartmentId());
//        //3、将公寓数据设置给房间对象
//        roomDetailVo.setApartmentItemVo(apartmentItemVo);
//
//        //保存缓存: 设置一个小时的过期时间
//        redisTemplate.opsForValue().set(cacheKey, roomDetailVo , 1, TimeUnit.HOURS);
//
//        return roomDetailVo;
//    }
}




